#include <cmath>
#include <ctype.h>
#include "highgui.h"
#include "cv.h"
using namespace std;

class CvPixel
{
public:
	unsigned char r, g, b;
	CvPixel(unsigned char R, unsigned char G, unsigned char B)
	{
		r = R;
		g = G;
		b = B;
	}
	CvPixel(){
		r = g = b = 0;
	}
	char* toString(){
		char *s = new char[15];
		sprintf(s, "(%d,%d,%d)", r, g, b);
		return s;
	}
};

class CvImage{
private:
	IplImage* image;
public:
	CvImage(){
		image = NULL;
	}
	CvImage(IplImage* Image){
		image = cvCreateImage( cvSize(Image->width, Image->height ),
			Image->depth, Image->nChannels );
		cvCopy(Image, image);
	}
	IplImage* getIplImage(){
		return image;
	}
	void readFile(const char* filename){
			image=cvLoadImage(filename, CV_LOAD_IMAGE_COLOR);
	}
	void saveFile(const char* filename){
		cvSaveImage(filename, image);
	}
	CvImage* resize_image(int Width, int Height, int interpolation=CV_INTER_LINEAR ){
		IplImage* dst = cvCreateImage( cvSize(Width,Height) , /*depth=*/8, /*channels=*/3);	
		cvResize(image, dst, interpolation);
		return new CvImage(dst);
	}
	int width(){
		return image->width;
	}
	int height(){
		return image->height;
	}


	CvPixel grabPixel(int x, int y)
	{
		CvPixel p;
		uchar * data = (uchar *)image->imageData;

		int base = y * (image->widthStep) + x * (image->nChannels);

		p.b=data[base];
		p.g=data[base+1];
		p.r=data[base+2];

		return p;
	}
	~CvImage()
	{
		cvReleaseImage( &image );
	}
};





class CvVideo{
private:
	CvCapture* capture;
	int Width, Height;
	double fps;
	double Length;
	int FrameCount;
public:
	CvVideo(){
	}
	void readFile(const char* filename){
		capture = cvCaptureFromAVI(filename);
		Width =  (int)cvGetCaptureProperty( capture, CV_CAP_PROP_FRAME_WIDTH );
		Height =  (int)cvGetCaptureProperty( capture, CV_CAP_PROP_FRAME_HEIGHT );
		fps = cvGetCaptureProperty( capture, CV_CAP_PROP_FPS );
		printf("fps = %lf     in %s\n", fps, filename);
		FrameCount = (int)cvGetCaptureProperty( capture, CV_CAP_PROP_FRAME_COUNT  );
		Length = frameCount()/frameRate();
	}
	CvVideo *copy(const char* dst_file){
		return cut(0, length(), dst_file);
	}
	void createFromImage(CvImage* img, float duration,  double fps, const char* dst_file)
	{
		CvVideoWriter *writer = 0;
		int imageW = img->width();
		int imageH = img->height();
		int isColor = 1;
		int frameCount = (int) (fps * duration);
		IplImage* image = img->getIplImage();

		writer=cvCreateVideoWriter(dst_file, CV_FOURCC('I','Y','U','V'),
			fps,cvSize(imageW,imageH),isColor);

		for(int count = 0; count < frameCount; count++)
			cvWriteFrame(writer,image);   

		cvReleaseVideoWriter(&writer);
		readFile(dst_file);
	}
	CvCapture* getCapture(){
		return capture;
	}
	CvImage* grabFrameByFrame(int frameID)
	{
		cvSetCaptureProperty( capture, CV_CAP_PROP_POS_FRAMES, frameID );
		return new CvImage(cvQueryFrame( capture ));
	}
	CvImage* grabFrameByTime(double time)
	{
		cvSetCaptureProperty( capture, CV_CAP_PROP_POS_MSEC, 1000 *  time );
		return new CvImage(cvQueryFrame( capture ));
	}
	int width()
	{
		return Width;
	}
	int height()
	{
		return Height;
	}
	double frameRate()
	{
		return fps;
	}
	int frameCount()
	{
		return FrameCount;
	}
	double length()
	{
		return FrameCount/fps;
	}
	CvVideo* fade_in(const char* dst_file, double fade_length)
	{
		int from_time_msec = 0;
		int to_time_msec = 1000 * Length;
		int i,j,k;
		int step;
		int time = 0;
		int channels;
		double ratio;

		IplImage* frame;
		uchar * data = 0;

		CvVideoWriter *writer = cvCreateVideoWriter(dst_file ,CV_FOURCC('I','Y','U','V'),
			fps,cvSize(Width, Height), 1);

		cvSetCaptureProperty( capture, CV_CAP_PROP_POS_FRAMES, 0);
		while(1) {
			frame = cvQueryFrame( capture );
			if(!frame)
				break;
			// release frame?
			step = frame->widthStep;
			channels = frame->nChannels;
			time = (int) cvGetCaptureProperty( capture, CV_CAP_PROP_POS_MSEC );
			if(time >= 0 && time < from_time_msec){
				cvWriteFrame(writer,frame);   
			}
			else if (time >= from_time_msec && time <= to_time_msec){
				ratio = time/(double)(to_time_msec - from_time_msec);
				data  = (uchar *)frame->imageData;
				for(i=0;i<Height;i++){ 
					for(j=0;j<Width;j++){
						for(k=0;k<channels;k++){
							data[i*step+j*channels+k] *= ratio;
						}
					}
				}
				cvWriteFrame(writer,frame);   
			}
			else{
				cvWriteFrame(writer,frame);   
			}
		}
		cvReleaseVideoWriter(&writer);
		CvVideo *v = new CvVideo();
		v->readFile(dst_file);
		return v;
	}
	CvVideo* fade_out(const char* dst_file, double fade_length)
	{
		int from_time_msec = 1000*(Length -  fade_length);
		int to_time_msec = 1000 * Length;
		int i,j,k;
		int step;
		int time = 0;
		int channels;
		double ratio;

		IplImage* frame;
		uchar * data = 0;

		CvVideoWriter *writer = cvCreateVideoWriter(dst_file ,CV_FOURCC('I','Y','U','V'),
			fps,cvSize(Width, Height), 1);

		cvSetCaptureProperty( capture, CV_CAP_PROP_POS_FRAMES, 0);
		while(1) {
			frame = cvQueryFrame( capture );
			if(!frame)
				break;
			// release frame?
			step = frame->widthStep;
			channels = frame->nChannels;
			time = (int) cvGetCaptureProperty( capture, CV_CAP_PROP_POS_MSEC );
			if(time >= 0 && time < from_time_msec){
				cvWriteFrame(writer,frame);   
			}
			else if (time >= from_time_msec && time <= to_time_msec){
				ratio = (to_time_msec - time)/(double)(to_time_msec - from_time_msec);
				data  = (uchar *)frame->imageData;
				for(i=0;i<Height;i++){ 
					for(j=0;j<Width;j++){
						for(k=0;k<channels;k++){
							data[i*step+j*channels+k] *= ratio;
						}
					}
				}
				cvWriteFrame(writer,frame);   
			}
			else{
				cvWriteFrame(writer,frame);   
			}
		}
		cvReleaseVideoWriter(&writer);
		CvVideo *v = new CvVideo();
		v->readFile(dst_file);
		return v;
	}
	CvVideo* join(CvVideo* video2, const char* dst_file)
	{
		IplImage* frame;	
		CvVideoWriter *writer = 0;
		int isColor = 1;
		writer=cvCreateVideoWriter(dst_file,CV_FOURCC('I','Y','U','V'),
			fps,cvSize(Width, Height),isColor);

		cvSetCaptureProperty( capture, CV_CAP_PROP_POS_FRAMES, 0);

		while(NULL != (frame = cvQueryFrame( capture ))) {
			cvWriteFrame(writer,frame);        
			// release frame?
		}

		CvCapture* capture2 = video2->getCapture();
		cvSetCaptureProperty( capture2, CV_CAP_PROP_POS_FRAMES, 0);
		while(NULL != (frame = cvQueryFrame( capture2))) {
			cvWriteFrame(writer,frame);        
			// release dst?
		}

		cvReleaseVideoWriter(&writer);

		CvVideo* v = new CvVideo();
		v->readFile(dst_file);
		return v;
	}

	CvVideo* resize(int dstWidth, int dstHeight, const char* dst_file)
	{
		IplImage* frame;	
		IplImage* dst;
		
		int isColor = 1;

		CvVideoWriter *writer=cvCreateVideoWriter(dst_file,CV_FOURCC('I','Y','U','V'),
			fps,cvSize(dstWidth, dstHeight),isColor);
		cvSetCaptureProperty( capture, CV_CAP_PROP_POS_FRAMES, 0);
		while(NULL != (frame = cvQueryFrame( capture ))) {
			dst = cvCreateImage( cvSize(dstWidth,dstHeight) , /*depth=*/8, /*channels=*/3);	
			cvResize(frame, dst);
			cvWriteFrame(writer,dst);        
			// release dst?
		}

		cvReleaseVideoWriter(&writer);

		CvVideo* v = new CvVideo();
		v->readFile(dst_file);
		return v;
	}

	CvVideo* cropVideo(int center_x, int center_y, int Width, int Height, const char* dst_file)
	{
		int x = center_x - Width/2;
		int y = center_y - Height/2; 
		IplImage* frame, *dst;	
		CvVideoWriter *writer = 0;

		int isColor = 1;

		writer=cvCreateVideoWriter(dst_file,CV_FOURCC('I','Y','U','V'),
			fps,cvSize(Width, Height),isColor);
		cvSetCaptureProperty( capture, CV_CAP_PROP_POS_FRAMES, 0);
		while(NULL != (frame = cvQueryFrame( capture ))) {
			cvSetImageROI(frame,cvRect(x, y, Width, Height));
			dst = cvCreateImage(cvSize(Width,Height), frame->depth, frame->nChannels);
			cvCopy(frame, dst); 
			cvWriteFrame(writer,dst);      
			// release dst?
		}

		cvReleaseVideoWriter(&writer);
		
		CvVideo* v = new CvVideo();
		v->readFile(dst_file);
		return v;
	}

	CvVideo* zoom(float w_rate, float h_rate , const char* dst_file)
	{
		IplImage* frame;	
		CvVideoWriter *writer = 0;
		int dstWidth = w_rate * Width;
		int dstHeight = h_rate * Height;
		int isColor = 1;

		writer=cvCreateVideoWriter(dst_file,CV_FOURCC('I','Y','U','V'),
			fps,cvSize(Width,Height),isColor);
		cvSetCaptureProperty( capture, CV_CAP_PROP_POS_FRAMES, 0);
		while(NULL != (frame = cvQueryFrame( capture ))) {
			IplImage* img  = cvCreateImage( cvSize(dstWidth,dstHeight) , /*depth=*/8, /*channels=*/3);	
			cvResize(frame, img);
			//IplImage* img = frame->resize_image((int)(Width*w_rate), (int)(Height*h_rate), 
			//	8 /*frame->depth*/, 3 /*frame->nChannels*/);
			int x = abs((img->width-Width)/2);
			int y = abs((img->height-Height)/2);
			cvSetImageROI(img,cvRect(x, y, Width, Height));
			IplImage* dst = cvCreateImage(cvSize(Width,Height), img->depth, img->nChannels);
			cvCopy(img, dst); 
			cvWriteFrame(writer,dst);        
			// release frame/dst/img?
		}

		cvReleaseVideoWriter(&writer);
		CvVideo* v = new CvVideo();
		v->readFile(dst_file);
		return v;

	}


	CvVideo* cut(double t_start,  double t_end, const char* dst_file)
	{
		IplImage* frame;

		int isColor = 1;
		int start_frame = fps*t_start;
		int end_frame = fps*t_end;

		cvSetCaptureProperty( capture, CV_CAP_PROP_POS_FRAMES, start_frame );	

		CvVideoWriter *writer = 0;
		writer=cvCreateVideoWriter(dst_file,CV_FOURCC('I','Y','U','V'),
			fps,cvSize(Width,Height),isColor);

		cvSetCaptureProperty( capture, CV_CAP_PROP_POS_FRAMES, start_frame);
		for(int i = start_frame; i<=end_frame; i++) {
			frame = cvQueryFrame( capture );
			if(!frame)
				break;

			cvWriteFrame(writer,frame);        
			//release?
		}

		cvReleaseVideoWriter(&writer);
		CvVideo* v = new CvVideo();
		v->readFile(dst_file);
		return v;
	}
	~CvVideo()
	{
		cvReleaseCapture( &capture );
	}
};






